import os
import socket
import json

def receive_file(sock, save_dir):
    metadata = json.loads(sock.recv(4096).decode().strip())
    file_path = os.path.join(save_dir, metadata['path'])
    
    os.makedirs(os.path.dirname(file_path), exist_ok=True)
    
    received_size = 0
    with open(file_path, 'wb') as f:
        while received_size < metadata['size']:
            chunk = sock.recv(min(8192, metadata['size'] - received_size))
            if not chunk:
                break
            f.write(chunk)
            received_size += len(chunk)
    
    return metadata['path']

def get_structure(directory):
    structure = {'dirs': [], 'files': {}}
    for root, dirs, files in os.walk(directory):
        for dir in dirs:
            full_path = os.path.join(root, dir)
            rel_path = os.path.relpath(full_path, directory)
            structure['dirs'].append(rel_path)
            
        for file in files:
            full_path = os.path.join(root, file)
            rel_path = os.path.relpath(full_path, directory)
            mtime = os.path.getmtime(full_path)
            size = os.path.getsize(full_path)
            structure['files'][rel_path] = {'mtime': mtime, 'size': size}
    return structure

def start_client(save_dir, host, port):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((host, port))
        
        # Receive sender's structure
        sender_structure = json.loads(s.recv(4096000).decode())
        local_structure = get_structure(save_dir)
        
        # Create missing directories
        for dir_path in sender_structure['dirs']:
            full_path = os.path.join(save_dir, dir_path)
            if not os.path.exists(full_path):
                os.makedirs(full_path)
                print(f"Created directory: {dir_path}")
        
        # Compare and determine needed files
        needed_files = []
        for path, info in sender_structure['files'].items():
            if path not in local_structure['files'] or \
               local_structure['files'][path]['mtime'] < info['mtime']:
                needed_files.append(path)
        
        # Send list of needed files
        s.send(json.dumps(needed_files).encode() + b'\n')
        
        # Receive needed files
        while True:
            try:
                file_path = receive_file(s, save_dir)
                print(f"Received: {file_path}")
            except:
                break
        
        print("Sync completed")

if __name__ == "__main__":
    save_dir = r"c:\sscms-7.3.0-win-x641"
    os.makedirs(save_dir, exist_ok=True)
    start_client(save_dir, "localhost", 212)